package com.nexus.all.service.impl;

import com.nexus.all.config.SpringParameterConfig;
import com.nexus.all.constants.RedisKeyNexus;
import com.nexus.all.enums.FileStatusEnum;
import com.nexus.all.model.FileBatch;
import com.nexus.all.service.IFileBatchService;
import com.nexus.bean.exeption.ServiceException;
import com.nexus.all.manager.FileBatchManager;
import com.nexus.bean.result.ResultCode;
import com.nexus.bean.utils.CollectionsUtil;
import com.nexus.bean.utils.FileUtils;
import com.nexus.bean.utils.LogUtils;
import com.nexus.bean.utils.UuIdUtils;
//import com.nexus.lock.RedissonLock;
import com.nexus.oss.service.OssService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;


/**
 * 文件上传批次表实现类
 *
 * @author zxt
 * @version 1.0
 * @CopyRright (c): <?>
 */
@Slf4j
@Service("fileBatchService")
@AllArgsConstructor
public class FileBatchServiceImpl implements IFileBatchService {

    private FileBatchManager fileBatchManager;
    //private RedissonLock redissonLock;
    private OssService ossService;
    private SpringParameterConfig springParameterConfig;

    private final String imageDirectory = "boot/nexus-all/images/";

    @Override
    @Transactional
    public String syncUploadImage(MultipartFile file, Long userId) {

        /**
         * 异步上传实现逻辑
         * 1.保存文件到本地，并将存储信息进行保存(nexus_file_batch表)
         * 2.提前把存储到oss的地址返回给前端(ossUrl+目录+生成文件名)
         * 3.主动或被动触发定时任务进行文件的推送(通过nexus_file_batch表拉取未上传的)，将其推送到oss
         */

        //将文件保存到本地
        File localFile = FileUtils.multipartFileToFile(file);
        if (Objects.isNull(localFile)) {
            throw new ServiceException(ResultCode.COMMON_MESSAGE, "请上传正确格式的相册");
        }

        //拼接上传后的目录
        StringBuilder builder = new StringBuilder(imageDirectory);
        builder.append(LocalDate.now() + "/");
        builder.append(localFile.getName());
        String remoteFileUri = builder.toString();

        //保存待上传文件信息
        FileBatch fileBatch = new FileBatch();
        fileBatch.setUserId(userId);
        fileBatch.setLocalFileUri(localFile.getPath());
        fileBatch.setRemoteFileUri(remoteFileUri);
        fileBatch.setFileStatus(FileStatusEnum.PENDING.getValue());
        fileBatchManager.save(fileBatch);

        //异步生效其实是Spring拿到当前对象的代理对象，这里如果直接调用就不是Spring的代理对象了事务就会失效，所以要获取当前对象的代理对象
        //主动触发异步上传，将需要上传的文件信息进行传递
        FileBatchServiceImpl proxy = (FileBatchServiceImpl) AopContext.currentProxy();
        proxy.dealPendingFileBatch(CollectionsUtil.asList(fileBatch), LogUtils.getTraceId());

        //提前返回文件上传后的地址给前端
        return springParameterConfig.getOssUrl() + remoteFileUri;
    }

    @Override
    @Async
    public void dealPendingFileBatch(List<FileBatch> fileBatchList, String traceId) {
        LogUtils.putAsyncTraceId(traceId);
        if (CollectionsUtil.isEmpty(fileBatchList)) {
            fileBatchList = fileBatchManager.getByPendingFileBatch();
        }
        if (CollectionsUtil.isEmpty(fileBatchList)) {
            return;
        }
        for (FileBatch fileBatch : fileBatchList) {
            log.info("推送时间邮件 时间邮件id:{},", fileBatch.getId());
            /*if (!redissonLock.lock(RedisKeyNexus.getLock("dealPendingFileBatch", fileBatch.getId()), 0, 10)) {
                log.info("处理待上传的文件,锁被占用");
                return;
            }*/
            try {
                uploadFileToOss(fileBatch);
            } catch (Exception e) {
                log.error("推送文件id:{},异常信息:{}", fileBatch.getId(), e);
                fileBatchManager.updateStatus(fileBatch.getId(), FileStatusEnum.FAIL.getValue(), CollectionsUtil.asList(FileStatusEnum.PROCESSING.getValue()));
            } finally {
                log.info("推送文件id:{},结束", fileBatch.getId());
                //redissonLock.release(RedisKeyNexus.getLock("dealPendingFileBatch", fileBatch.getId()));
            }
        }
    }

    public void uploadFileToOss(FileBatch fileBatch) {
        //修改状态为处理中
        boolean isTrue = fileBatchManager.updateStatus(fileBatch.getId(), FileStatusEnum.PROCESSING.getValue(), CollectionsUtil.asList(FileStatusEnum.PENDING.getValue(), FileStatusEnum.FAIL.getValue()));
        if (!isTrue) {
            log.info("推送中，跳过推送，fileBatchId:{}", fileBatch.getId());
        }

        File localFile = new File(fileBatch.getLocalFileUri());
        MultipartFile multipartFile = FileUtils.fileToMultipartFile(localFile);
        if (Objects.isNull(multipartFile)) {
            log.info("已经推送过，结束推送，fileBatchId:{}", fileBatch.getId());
            fileBatchManager.updateStatus(fileBatch.getId(), FileStatusEnum.SUCCESS.getValue(), CollectionsUtil.asList(FileStatusEnum.PROCESSING.getValue()));
            return;
        }

        String toUrl = ossService.uploadToUrl(multipartFile, fileBatch.getRemoteFileUri());
        //判断上传成功的路径和未上传路径是否一致时
        if (!(springParameterConfig.getOssUrl() + fileBatch.getRemoteFileUri()).equals(toUrl)) {
            log.error("oss上传路径不一致，上传url:{}，fileBatchId:{}", toUrl, fileBatch.getId());
            throw new ServiceException(ResultCode.COMMON_MESSAGE, "路径不一致");
        }

        //删除本地文件
        FileUtils.deleteFile(localFile);
        fileBatchManager.updateStatus(fileBatch.getId(), FileStatusEnum.SUCCESS.getValue(), CollectionsUtil.asList(FileStatusEnum.PROCESSING.getValue()));
    }

}
